package edu.whu.assignment7.aspect;

import edu.whu.assignment7.exception.ExceptionResponse;
import lombok.Data;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

@Component
@Aspect
public class MonitorAspect {

    /*
    * 统计表格，key是方法的签名，value是统计的数据
    * */
    private final Map<String, MonitorData> dataMap = Collections.synchronizedMap(new HashMap<>());

    Logger logger = LoggerFactory.getLogger(getClass());

    //@Around("@within(org.springframework.stereotype.Service)")
    @Around("execution(* edu.whu.assignment7.controller.*.*(..))")
    public Object monitor(ProceedingJoinPoint joinPoint) throws Throwable {
        //判断Map中是否有该签名的key，没有没有，就加上,同时获得该签名的数据对象
        String signature = joinPoint.getSignature().toString();
        MonitorData data = null;
        if(dataMap.containsKey(signature)) {
            data = dataMap.get(signature);
        }
        else {
            data = new MonitorData();
            dataMap.put(signature, data);
        }

        long totalTime = data.avgTime * data.invokeCount;
        //计算相应时间
        long t1 = Calendar.getInstance().getTimeInMillis();
        //执行api请求
        Object result = joinPoint.proceed();
        long t2 = Calendar.getInstance().getTimeInMillis();
        long time = t2 - t1;
        logger.info("调用时间为：" + time);
        //调用次数加一
        data.invokeCount++;
        //计算最长时间、最短时间、平均时间
        if(time > data.maxTime) {
            data.maxTime = time;
        }
        if(data.minTime == 0 || time < data.minTime) {
            data.minTime = time;
        }
        data.avgTime = (totalTime + time) / data.invokeCount;

        logger.info(signature + ": " + data);

        return result;
    }

    @AfterThrowing(pointcut = "execution(* edu.whu.assignment7.controller.*.*(..))", throwing = "exception")
    public void afterPointCutThrow(JoinPoint joinPoint, Exception exception) {
        //判断Map中是否有该签名的key，没有没有，就加上,同时获得该签名的数据对象
        String signature = joinPoint.getSignature().toString();
        MonitorData data = dataMap.containsKey(signature)
                ? dataMap.get(signature)
                : dataMap.put(signature,new MonitorData());
        //异常次数加一
        data.exceptionCount++;
        logger.error(("afterThrowing:异常为: " + exception));
    }

    /*
    * 内部类，统计的数据，将他们封装到一起
    * */
    @Data
    private class MonitorData {
        int invokeCount = 0;
        long maxTime = 0;
        long minTime = 0;
        long avgTime = 0;
        int exceptionCount = 0;
    }
}
